﻿using System;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using CoNatural.Data;

namespace CoNatural.Data.VisualStudio.AddIn.Tools {
   /// <summary>
   /// Used to generate temporary assembly from command's source code in a temporary AppDomain
   /// and return a new instance to the main AppDomain (AddIn).
   /// </summary>
   [Serializable]
   public class CommandLoader : MarshalByRefObject {
      private ICommand[] commands;
		private IDbProvider dbProvider = new SqlClient.SqlClientDbProvider();

      public CommandLoader() {}

		public string[] Commands { 
			get {
				if (commands != null && commands.Length > 0) {
					var cmds = new string[commands.Length];
					for (int i = 0; i < commands.Length; i++)
						cmds[i] = dbProvider.ScriptName(commands[i]);
					return cmds;
				}
				return null;
			} 
		}

		private ICommand this[int index] {
			get {
				if (commands != null && commands.Length > index && index >= 0) {
					return commands[index];
				}
				return null;
			}
		}

      /// <summary>
      /// Loads ICommand(s) from C# source code in local AppDomain.
      /// </summary>
		/// <param name="path">The path of the C# file with commands.</param>
      /// <param name="source">The source code.</param>
		/// <param name="referencedAssemblies">The list of referenced assemblies.</param>
		public void LoadCommands(string path, string source, string[] referencedAssemblies) {
         // load referenced assemblies to temporary AppDomain
         foreach (string referencedAssembly in referencedAssemblies) {
            try {
               System.Reflection.Assembly.LoadFrom(referencedAssembly);
            }
            catch (System.IO.FileNotFoundException ex) {
               //System.Windows.Forms.MessageBox.Show(ex.FusionLog);
               throw new Exception(string.Format("CommandLoader error loading referenced assembly {0}.\n{1}",
                  referencedAssembly, ex.Message));
            }
         }

			// compile it
			Type[] types = CommandCompiler.CompileCommand(source, referencedAssemblies);

			// create instances
			if (types.Length > 0) {
				commands = new ICommand[types.Length];
				for(int i = 0; i < types.Length; i++)
					commands[i] = (ICommand)Activator.CreateInstance(types[i]);
			}
      }

      /// <summary>
      /// Creates SqlCommand from internal ICommand
      /// </summary>
		/// <param name="index">The index of the loaded command.</param>
      /// <returns>The SqlCommand.</returns>
      public SqlCommand ToSqlCommand(int index) {
         SqlCommand sqlCmd = null;
			ICommand command = this[index];
         if (command != null ) {
				var scriptProvider = new EmbeddedResourceCommandScriptProviderBase();
            sqlCmd = new SqlCommand(command.GetScript() ?? scriptProvider.GetScript(command));
            foreach (var p in CoNatural.Data.Parameter.GetParameters(dbProvider, command.GetType()))
               sqlCmd.Parameters.Add((SqlParameter)dbProvider.CreateDataParameter(
						p.Name,
						p.DbType,
						p.Direction,
						p.Size,
						p.Precision,
						p.Scale,
						p.IsNullable,
						p.UdtName,
						null
					));
         }
         return sqlCmd;
      }

      /// <summary>
      /// Deploy command as stored procedure
      /// </summary>
		/// <param name="connectionString">The connection string.</param>
		/// <param name="index">The index of the loaded command.</param>
      /// <returns>True if command deployed succesfully, false otherwise.</returns>
      public bool Deploy(string connectionString, int index) {
			ICommand command = this[index];
         if (command != null) {
            IConnection conn = new SqlClient.SqlClientConnection(connectionString);
            ICommandDeployer deployer = new SqlServer.SqlServerCommandDeployer();
            deployer.Deploy(conn, new ICommand[] { command });
				return true;
         }
			return false;
      }
   }
}
